﻿using System;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Caching;
using Contracts.Caching;
using Types.Constants;
using Types.Enumerations;
using Types.Enumerations.Cache;


namespace Helper
{
    public partial class CacheHelper : CacheItems
    {
        #region [CACHE METHODLARI] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        #region [ANA CACHE METHODLARI] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        public static DataTable CacheOku(CacheItemNames pCacheItemName)
        {
            return CacheOku(pCacheItemName, string.Empty, string.Empty, 0);
        }
        public static DataTable CacheOku(CacheItemNames pCacheItemName, int pTopN)
        {
            return CacheOku(pCacheItemName, string.Empty, string.Empty, pTopN);
        }

        public static DataTable CacheOku(CacheItemNames pCacheItemName, string pFilterExpression)
        {
            return CacheOku(pCacheItemName, pFilterExpression, string.Empty, 0);
        }
        public static DataTable CacheOku(CacheItemNames pCacheItemName, string pFilterExpression, int pTopN)
        {
            return CacheOku(pCacheItemName, pFilterExpression, string.Empty, pTopN);
        }

        public static DataTable CacheOku(CacheItemNames pCacheItemName, string pFilterExpression, string pSortExpression)
        {
            return CacheOku(pCacheItemName, pFilterExpression, pSortExpression, 0);
        }
        public static DataTable CacheOku(CacheItemNames pCacheItemName, string pFilterExpression, string pSortExpression, int pTopN)
        {
            bool isPartialCache = true;
            DataSet oDsRef = null;
            DataTable oDt = GetCacheIfPartial(pCacheItemName, pFilterExpression, pSortExpression, pTopN, ref isPartialCache, ref oDsRef);
            if (isPartialCache) return oDt;

            DataSet oDs = ReadCacheObject(pCacheItemName);

            if (oDs.Tables.Count > 0)
            {
                if (!String.IsNullOrEmpty(pFilterExpression) || !String.IsNullOrEmpty(pSortExpression) || pTopN > 0)
                    return GetDataSetAsDataTable(oDs, pFilterExpression, pSortExpression, pTopN);
                else
                    return oDs.Tables[0];   //NOT: Cache okunduktan sonra filtreleme işlemi yapıldığında DataSet yapısı değiştiğinden TypedDataSet'e cast edilemiyor
            }
            else
                return new DataTable();
        }

        public static DataSet CacheOkuDataSetDondur(CacheItemNames pCacheItemName)
        {
            bool isPartialCache = true;
            DataSet oDsRef = null;
            GetCacheIfPartial(pCacheItemName, string.Empty, string.Empty, 0, ref isPartialCache, ref oDsRef);
            if (isPartialCache) return oDsRef;

            return ReadCacheObject(pCacheItemName);
        }

        private static DataTable GetDataSetAsDataTable(DataSet pDs, string pFilterExpression, string pSortExpression, int pTopN)
        {
            return pDs.Tables[0].FilterDataTable(pFilterExpression, pSortExpression, pTopN);
        }

        private static DataSet ReadCacheObject(CacheItemNames pCacheItemName)
        {
            string name = pCacheItemName.GetEnumDescription();
            DataSet oDs = CacheProvider.Provider.GetCacheItem(name);
            if (oDs != null) return oDs;
            else
            {
                throw new ApplicationException("DataSet Null döndü. CacheItemName app.config ve/veya web.config dosyasında tanımlı olmayabilir ya da hatalı tanımlanmış olabilir !");
            }
        }

        public static void CacheTemizle(CacheItemNames pCacheItemName)
        {
            //if (pCacheItemName == CacheItemNames.Ortak_KISI_Filtered
            //        || pCacheItemName == CacheItemNames.Organizasyon_BIRIM_Filtered
            //        || pCacheItemName == CacheItemNames.Organizasyon_COGRAFI
            //        || pCacheItemName == CacheItemNames.YeniYetki_Sayfalar
            //        || pCacheItemName == CacheItemNames.YeniYetki_Fonksiyonlar
            //        || pCacheItemName == CacheItemNames.YeniYetki_BirimYetki
            //        )
            //{
            //    RemoveFromLocalCache(pCacheItemName);
            //}
            //else
            //{
            //    string name = Utils.GetEnumDescription(pCacheItemName);
            //    CacheProvider.Provider.RefreshCacheItem(name);

            //    CacheTemizlemeIsleminiLogla(name);
            //}

            string name = pCacheItemName.GetEnumDescription();
            CacheProvider.Provider.RefreshCacheItem(name);

            CacheTemizlemeIsleminiLogla(name);
        }

        public static void CacheTemizlemeIsleminiLogla(string pCacheItemName)
        {
            string mesaj = string.Format(@"CACHE TEMİZLENDİ.
                                       | CacheItemName: {0}
                                       | Çağıran Sayfa: {1}
                                       | Çağıran Method: {2}
                                       | KullanıcıAdi: {3}
                                       ", pCacheItemName, SayfaUrl, MethodName, KullaniciAdi);
            mesaj = mesaj.Replace("  ", "");
            EventLog.WriteEntry(CommonConstants.EventLogSource.Application, mesaj, EventLogEntryType.Information, CacheEventLogId.CacheCleaned);
        }

        #endregion

        #region [PARÇALI CACHE METHODLARI] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        private static DataTable GetCacheIfPartial(CacheItemNames pCacheItemName, string pFilterExpression, string pSortExpression, int pTopN, ref bool pPartial, ref DataSet pDataSet)
        {
            CacheItemNames cacheItemNameSub = 0;
            string primeryKeyFieldName = string.Empty;

            GetSubCacheItemParameters(pCacheItemName, ref cacheItemNameSub, ref primeryKeyFieldName, ref pPartial);
            if (!pPartial) return null;

            return FillThisCachePartially(pCacheItemName, cacheItemNameSub, primeryKeyFieldName, pFilterExpression, pSortExpression, pTopN, ref pDataSet);
        }

        private static void GetSubCacheItemParameters(CacheItemNames pCacheItemName, ref CacheItemNames cacheItemNameSub, ref string primeryKeyFieldName, ref bool pPartial)
        {
            switch (pCacheItemName)
            {
                //case CacheItemNames.Ortak_KISI_Filtered:
                //    cacheItemNameSub = CacheItemNames.Ortak_KISI_Filtered_Gunluk;
                //    primeryKeyFieldName = "KisiKey";
                //    break;
                //case CacheItemNames.Organizasyon_BIRIM_Filtered:
                //    cacheItemNameSub = CacheItemNames.Organizasyon_BIRIM_Filtered_Gunluk;
                //    primeryKeyFieldName = "BirimKey";
                //    break;
                //case CacheItemNames.Organizasyon_COGRAFI:
                //    cacheItemNameSub = CacheItemNames.Organizasyon_COGRAFI_Gunluk;
                //    primeryKeyFieldName = "CografiKey";
                //    break;
                //case CacheItemNames.YeniYetki_Sayfalar:
                //    cacheItemNameSub = CacheItemNames.YeniYetki_SayfalarGunluk;
                //    primeryKeyFieldName = "SayfaKey,SayfaGrupKey";
                //    break;
                //case CacheItemNames.YeniYetki_Fonksiyonlar:
                //    cacheItemNameSub = CacheItemNames.YeniYetki_FonksiyonlarGunluk;
                //    primeryKeyFieldName = "KullaniciFonksiyonKey";
                //    break;
                //case CacheItemNames.YeniYetki_BirimYetki:
                //    cacheItemNameSub = CacheItemNames.YeniYetki_BirimYetkiGunluk;
                //    primeryKeyFieldName = "BirimYetkiKey";
                //    break;
                default: pPartial = false; return;
            }
        }

        private static DataTable FillThisCachePartially(CacheItemNames pCacheItemNameMain, CacheItemNames pCacheItemNameSub, string pPrimaryKeyFieldName, string pFilterExpression, string pSortExpression, int pTopN, ref DataSet pDataSet)
        {
            DataSet oDs = GetFromLocalCache(pCacheItemNameMain);
            DataTable oDtMain = oDs.Tables.Count > 0 ? oDs.Tables[0] : null;
            if (oDtMain == null) return oDtMain;

            string cacheItemName = pCacheItemNameSub.GetEnumDescription();

            DataSet oDsg = null;
            bool subCacheDoldur = false;
            if (CacheProvider.ProviderSettings.ProviderName == EnumCacheProviders.VelocityCache)
            {
                oDsg = CacheProvider.Provider.GetCacheItemIfNewer(cacheItemName);
                subCacheDoldur = oDsg != null ? true : false;    //DataSet null değilse cache güncellemesi ve union işlemi yapılacak demektir
            }
            else
            {
                subCacheDoldur = !CacheProvider.Provider.HasCacheItem(cacheItemName);
            }

            if (subCacheDoldur)
            {
                if (oDsg == null) oDsg = ReadCacheObject(pCacheItemNameSub);      //oDsg null ise cache provider AppFabric değildir
                DataTable oDtSub = oDsg.Tables.Count > 0 ? oDsg.Tables[0] : null;

                if (oDtSub != null && oDtSub.Rows.Count > 0) oDtMain.Union(oDtSub, LoadOption.Upsert, pPrimaryKeyFieldName, true);
            }

            if ((oDtMain.Rows.Count > 0) && (!String.IsNullOrEmpty(pFilterExpression) || !String.IsNullOrEmpty(pSortExpression) || pTopN > 0))
                return oDtMain.FilterDataTable(pFilterExpression, pSortExpression, pTopN);

            pDataSet = oDs;
            return oDtMain;
        }

        private static object LocalCacheSyncRoot = new object();
        private static DataSet GetFromLocalCache(CacheItemNames pCacheItemName)
        {
            string cacheItemName = pCacheItemName.GetEnumDescription();
            DataSet oDs = null;

            if (HttpRuntime.Cache[cacheItemName] != null)
            {
                return HttpRuntime.Cache[cacheItemName] as DataSet;
            }

            lock (LocalCacheSyncRoot)
            {
                if (HttpRuntime.Cache[cacheItemName] != null)
                {
                    return HttpRuntime.Cache[cacheItemName] as DataSet;
                }

                switch (pCacheItemName)
                {
                    //case CacheItemNames.Ortak_KISI_Filtered:
                    //    oDs = ServiceProxyCreator<IKisi, DataSet>.CallService(p => p.KisiSorgulaCache());
                    //    break;
                    //case CacheItemNames.Organizasyon_BIRIM_Filtered:
                    //    oDs = ServiceProxyCreator<IBirimAraTextBox, DataSet>.CallService(p => p.ReturnBIRIMDataSetToCache());
                    //    break;
                    //case CacheItemNames.Organizasyon_COGRAFI:
                    //    oDs = ServiceProxyCreator<ICografiYerAraTextBox, DataSet>.CallService(p => p.ReturnCOGRAFIDataSetToCache());
                    //    break;
                    //case CacheItemNames.YeniYetki_Sayfalar:
                    //    oDs = ServiceProxyCreator<IYeniYetkiCache, DataSet>.CallService(p => p.CacheSayfalar());
                    //    break;
                    //case CacheItemNames.YeniYetki_Fonksiyonlar:
                    //    oDs = ServiceProxyCreator<IYeniYetkiCache, DataSet>.CallService(p => p.CacheFonksiyonlar());
                    //    break;
                    //case CacheItemNames.YeniYetki_BirimYetki:
                    //    oDs = ServiceProxyCreator<IYeniYetkiCache, DataSet>.CallService(p => p.CacheBirimYetkileri());
                    //    break;
                    default:
                        break;
                }

                if (HttpRuntime.Cache[cacheItemName] != null)
                {
                    return HttpRuntime.Cache[cacheItemName] as DataSet;
                }

                CheckIfSubCacheWillBeCleaned(pCacheItemName);

                DateTime cacheExpireTime = DateTime.Now.Date.AddDays(1).AddMinutes(10);
                onRemove = new CacheItemRemovedCallback(RemovedCallback);
                HttpRuntime.Cache.Add(cacheItemName, oDs, null, cacheExpireTime, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, onRemove);
                EventLog.WriteEntry(CommonConstants.EventLogSource.Application, "Local Cache Added: " + cacheItemName, EventLogEntryType.Information, CacheEventLogId.CacheFilled);
                return oDs;
            }
        }

        private static void CheckIfSubCacheWillBeCleaned(CacheItemNames pCacheItemName)
        {
            if (CacheProvider.ProviderSettings.ProviderName == EnumCacheProviders.CustomInMemoryCache)
            {
                CacheItemNames cacheItemNameSub = 0;
                string primeryKeyFieldName = string.Empty;
                bool partial = true;
                GetSubCacheItemParameters(pCacheItemName, ref cacheItemNameSub, ref primeryKeyFieldName, ref partial);
                CacheTemizle(cacheItemNameSub);
            }
        }

        private static void RemoveFromLocalCache(CacheItemNames pCacheItemName)
        {
            string cacheItemName = pCacheItemName.GetEnumDescription();
            HttpRuntime.Cache.Remove(cacheItemName);
        }

        private static CacheItemRemovedCallback onRemove;
        private static void RemovedCallback(string pKey, Object pValue, CacheItemRemovedReason pReason)
        {
            LogRemovedCache(pKey, pReason);
            ReloadRemovedCache(pKey);
        }

        private static void LogRemovedCache(string pKey, CacheItemRemovedReason pReason)
        {
            string mesaj = string.Format(@"CACHE TEMİZLENDİ (WCF).
                                       | CacheItemName: {0}
                                       | Çağıran Sayfa: {1}
                                       | KullanıcıAdi: {2}
                                       | Neden: {3}
                                       | StackTraceList: 
                                        {4}
                                       ", pKey, SayfaUrl, KullaniciAdi, pReason.ToString(), StackTraceList);
            mesaj = mesaj.Replace("  ", "");
            EventLog.WriteEntry(CommonConstants.EventLogSource.Application, mesaj, EventLogEntryType.Information, CacheEventLogId.CacheCleaned);
        }

        private static void ReloadRemovedCache(string pKey)
        {
            foreach (CacheItemNames item in Enum.GetValues(typeof(CacheItemNames)))
            {
                if (item.GetEnumDescription() == pKey)
                {
                    GetFromLocalCache(item);
                    break;
                }
            }
        }

        public static string KullaniciAdi
        {
            get
            {
                if (HttpContext.Current != null && HttpContext.Current.Session != null && HttpContext.Current.Session["EBS_KullaniciAdi"] != null)
                    return HttpContext.Current.Session["EBS_KullaniciAdi"].ToString();
                else
                    return string.Empty;
            }
        }
        public static string SayfaUrl { get { return HttpContext.Current == null ? string.Empty : HttpContext.Current.Request.ServerVariables["URL"].ToString(); ;} }
        public static string MethodName
        {
            get
            {
                StackTrace stackTrace = new StackTrace();
                return stackTrace.GetFrame(2).GetMethod().ToString();
            }
        }
        public static string StackTraceList
        {
            get
            {
                StackTrace stackTrace = new StackTrace();
                return stackTrace.ToString();
            }
        }

        #endregion

        #endregion
    }
}
